package com.xdf.data_transmitter.utils;

import com.xdf.data_transmitter.model.*;
import com.xdf.data_transmitter.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class DataTransmitter {
    private static final String PROCESSTABLE="g_cjflowbilldetailprocess";
    private static final String JRKTABLE="g_jrkbill";

    @Autowired
    private UploadProcessService uploadProcessService;
    @Autowired
    private UploadRecordService uploadRecordService;
    @Autowired
    private GCjflowbilldetailprocessService processYieldService;
    @Autowired
    private ClientPushlistService clientPushlistService;
    @Autowired
    private ClientPushlistDetailService clientPushlistDetailService;
    @Autowired
    private PlanReportService planReportService;
    @Autowired
    private GPrintingdyeingnameService millService;
    @Autowired
    private GJrkbillService rollService;
    @Autowired
    private GCpwarehousingService cpInService;

    /**
     * 上传表 g_cjflowbilldetailprocess 还未上传的数据
     */
    public void uploadProcess(){
        Integer lastIId = getLastIId(PROCESSTABLE);// 获取最后一次上传的主键值，大于其继续上传

        // 参考表 process_mapping 是否设置了可上传的工序
        List<GCjflowbilldetailprocess> processYields = processYieldService.selectAfter(lastIId);
        if( processYields == null || processYields.size()<=0 ) {
            log.info("表 upload_record 针对数据表" + PROCESSTABLE + " 的最后一笔上传数据的主键是" + lastIId + "，该主键值之后没有数据，退出上传工序的方法。");
            log.info("请检查表 process_mapping 中有没有设置该客户需要上传的工序");
            return;
        }

        for( int i=0;i<processYields.size();i++ ){// 之前使用 forEach 循环，有莫名其妙的错误，内部的一条都不执行，直接跳出循环了
            // 每次工序都无条件更新表 plan_report.isStart
            String mill = millService.getMillName();
            String vatNum = processYields.get(i).getB_ID();
            planReportService.updateStart(mill,vatNum);

            // 拿当前刷卡数据和本地表 upload_process 对应缸号的一行对比数据
            // 1. 工序在两个维度上都没有前进则返回0 - 不用上传数据
            // 2. 工序仅在缸号维度上有前进则返回1 - 仅上传到 client_pushlist_Detail
            // 3. 工序在两个维度上都有前进则返回2 - 上传两个表
            int uploadLevel = updateUploadProcess(processYields.get(i));// 更新表 upload_process
            if( uploadLevel ==0 ) continue;// lambda 表达式中 return 表示退出本次循环继续下一次，0表示不要上传数据

            // 更新表 client_pushlist_Detail
            // 先删除当前缸号的一行数据，再新增最新的数据
            // 目的为更新每个缸号最新的刷卡进度
            List<ClientPushlistDetail> cpdList = processYieldService.getClientPushlistDetail(processYields.get(i).getB_ItemID());
            if( cpdList == null || cpdList.size()<=0 ) continue;
            ClientPushlistDetail cpd = cpdList.get(0);
            if( cpd == null ) continue;
            // 删除该缸号老的数据
            clientPushlistDetailService.deleteByMillVatnum(cpd.getMill(),cpd.getVat_num());
            // 修改对象实例的两个字段：process_index、process_count
            updateProcessProgress(cpd);
            clientPushlistDetailService.insertSelective(cpd);
            processYieldService.setUpload(1,processYields.get(i).getB_ItemID());// 为工序产量表标记已上传

            // 仅更新云端 client_pushlist_Detail，不用更新 client_pushlist
            // - 在颜色维度上没有前进
            if( uploadLevel <=1 ) continue;

            // 更新表 client_pushlist，所有经过了规则检验的数据都直接上传到本表，无须删除数据
            // 规则：颜色下只上传每次轮询时最快的一个工序，落后的工序不再上传
            List<ClientPushlist> list = processYieldService.getClientPushlist(processYields.get(i).getB_ItemID());
            if( list == null || list.size()<=0 ) continue;
            ClientPushlist clientPushlist = list.get(0);
            Integer yieldIId = processYields.get(i).getB_ItemID();
            clientPushlist.setErp_iid(yieldIId);// 写入 G_CJFlowBillDetailProcess.B_ItemID
            clientPushlistService.insertSelective(clientPushlist);
        }

        // 向表 upload_record 写入本次上传的最后一笔主键值
        UploadRecord ur = new UploadRecord();
        ur.setUpload_table(PROCESSTABLE);
        Integer iid = processYields.get(processYields.size()-1).getB_ItemID();
        iid = iid == null?0:iid;
        ur.setTable_iid(iid);
        ur.setUpdate_count(processYields.size());
        uploadRecordService.insertSelective(ur);
    }

    /**
     * 上传打卷数据：
     * 1. 云端 g_cpWarehousing 更新打卷入库数量
     * 2. 更新两个产量表：client_pushlist 和 client_pushlist_Detail
     */
    public void uploadRoll(){
        Integer lastIId = getLastIId(JRKTABLE);// 获取最后一次上传的主键值，大于其继续上传

        // 查询表 g_jrkbill 主键大于 lastIId 的所有数据，处理后上传云端
        // 对应存储过程是 usp_getJrkAfter ，在表 g_jrkbill 中按照缸号、入库时间统计匹数、公斤、米数
        List<GCpwarehousing> list1 = rollService.selectAfter(lastIId);
        if( list1 == null || list1.size()<=0 ) {
            log.info("上次上传的最后一个主键值是"+lastIId+"，此后没有需要上传的数据！");
            return;
        }
        // 统计即将上传的数据，返回一行两个字段：maxIId（本次上传的最后一个iid），uploadCount（本次上传的总行数）
        // 本批次最大的主键值，本批次总共行数
        List<JrkStatistics> jrkList = rollService.getUploadJrkStatistics(lastIId);

        for(int i=0;i<list1.size();i++){
            // 无条件写入g_cpWarehousing
            cpInService.insertSelective(list1.get(i));

            // 根据 uploadLevel 执行上传任务：
            // 0 - 不上传
            // 1 - 仅更新表 client_pushlist_Detail
            // 2 - 更新两个表
            int uploadLevel = updateUploadProcess(list1.get(i));
            if( uploadLevel ==0 ) continue;

            String vat_num = list1.get(i).getVat_num();
            String process_name = list1.get(i).getProcessName();
            Date dtrk = list1.get(i).getB_DTRk();
            List<ClientPushlistDetail> list2 = rollService.getClientPushlistDetail4JrkBill(vat_num,process_name,dtrk);
            if( list2 == null || list2.size()<=0 ) continue;
            ClientPushlistDetail cpd = list2.get(0);
            if( cpd == null ) continue;

            clientPushlistDetailService.deleteByMillVatnum(cpd.getMill(),cpd.getVat_num());
            updateProcessProgress(cpd);
            clientPushlistDetailService.insertSelective(cpd);
            rollService.updateUpload(dtrk);// 根据打卷入库时间批量更改 g_jrkbill.JudgeUpload = 1

            if( uploadLevel<=1 ) continue;

            ClientPushlist cp = new ClientPushlist();
            cp.setClient_id(cpd.getClient_id());
            cp.setClient(cpd.getClient());
            cp.setMill(cpd.getMill());
            cp.setProduct(cpd.getProduct());
            cp.setSpecification(cpd.getSpecification());
            cp.setColor(cpd.getColor());
            cp.setColor_no(cpd.getColor_no());
            cp.setVat_count(cpd.getVat_count());
            cp.setProgress(cpd.getProgress());
            cp.setVat_num(cpd.getVat_num());
            cp.setFinish_date(cpd.getFinish_date());
            cp.setOrder_no(cpd.getOrder_no());
            cp.setPairs(cpd.getPairs());
            cp.setKilo(cpd.getKilo());
            cp.setColor_cloth_name(cpd.getColor_cloth_name());
            cp.setOrderdtlpk(cpd.getOrderdtlpk());
            clientPushlistService.insertSelective(cp);
        }

        // 向表 upload_record 写入本次上传的最后一笔主键值
        if( jrkList == null || jrkList.size()<=0 ) {
            log.info("统计表 G_JRKBill 的大于主键值" + lastIId + "获取的集合为空，中断向本地表 upload_record 写入数据的操作！");
            return;
        }
        JrkStatistics js = jrkList.get(0);
        if( js == null ) {
            log.info("统计表 G_JRKBill 的大于主键值" + lastIId + "获取的集合为空，中断向本地表 upload_record 写入数据的操作！");
            return;
        }
        UploadRecord ur = new UploadRecord();
        ur.setUpload_table(JRKTABLE);
        Integer iid = js.getMaxIId();
        iid = iid == null?0:iid;
        ur.setTable_iid(iid);
        ur.setUpdate_count(js.getUploadCount());
        uploadRecordService.insertSelective(ur);
    }

    // 打卷数据更新本地表 upload_process
    // 0 - 不要更新任何数据
    // 1 - 更新云端 client_pushlist
    // 2 - 更新云端两个表
    private int updateUploadProcess(GCpwarehousing cpIn){
        if( cpIn == null ) return 0;
        String newProcess = cpIn.getProcessName();
        String vatNum = cpIn.getVat_num();
        List<UploadProcess> list2 = uploadProcessService.getOrderPlanByVat(vatNum,newProcess, cpIn.getB_DTRk());
        if( list2 == null || list2.size()<=0 ) return 0;
        UploadProcess upNew = list2.get(0);
        if( upNew == null ) return 0;


        List<UploadProcess> list1 = uploadProcessService.selectByVatnum(vatNum);
        UploadProcess upCur = new UploadProcess();
        if( list1 != null && list1.size()>0 )
            upCur = list1.get(0);

        Integer newIndex = 0;
        Integer curIndex = 0;
        List<PlanProcess> ppList = uploadProcessService.getProcessByVatnum(vatNum);
        if( ppList == null || ppList.size()<=0 ) return 0;
        for(int i=0;i<ppList.size();i++){
            if( ppList.get(i).getProcess_name().equals(newProcess) )
                newIndex = ppList.get(i).getSeq();
            if( ppList.get(i).getProcess_name() == upCur.getProcess_name() )
                curIndex = ppList.get(i).getSeq();
        }
        if( newIndex<=curIndex ) return 0;

        upCur.setBill_code(upNew.getBill_code());
        upCur.setOrder_iid(upNew.getOrder_iid());
        upCur.setPlan_iid(upNew.getPlan_iid());
        upCur.setVat_num(upNew.getVat_num());
        upCur.setProcess_name(upNew.getProcess_name());
        upCur.setProcess_index(newIndex);
        upCur.setYield_time(upNew.getYield_time());
        upCur.setPlan_time(upNew.getPlan_time());
        upCur.setOrder_time(upNew.getOrder_time());
        upCur.setReleaseDate(upNew.getReleaseDate());

        // 使用新值修改当前值后，将其写回表 upload_process
        int save = uploadProcessService.save(upCur);

        // 颜色维度有超过当前工序序号的则返回1，没有则返回2
        List<UploadProcess> list3 = uploadProcessService.selectOrderAfterProcess(upNew.getOrder_iid(),newIndex,upNew.getReleaseDate());
        return list3.size()>0?1:2;
    }

    /**
     * 更新 ClientPushlistDetail 对象实例的当前工序和总工序数
     * 后返回该实例对象
     * @param cpd
     * @return
     */
    private ClientPushlistDetail updateProcessProgress(ClientPushlistDetail cpd){
        if( cpd == null ) return cpd;
        String vatNum = cpd.getVat_num();
        String processName = cpd.getProgress();
        if( vatNum == null || vatNum.length()<=0 ) return cpd;
        if( processName == null || processName.length()<=0 ) return cpd;
        List<PlanProcess> processes = uploadProcessService.getProcessByVatnum(vatNum);
        if( processes == null || processes.size()<=0 ) return cpd;
        int seq = 0;
        for(int i=0;i<processes.size();i++){
            System.out.println("当前工序：" + processes.get(i).getProcess_name() + ",刷卡工序是：" + processName);
            if( processes.get(i).getProcess_name().equals(processName) ){
                seq = processes.get(i).getSeq();
                break;// 找到即跳出整个循环
            }
        }
        cpd.setProcess_index(seq);
        cpd.setProcess_count(processes.size());
        return cpd;
    }

    // 获取指定表上一次上传的最后笔主键值，下一次上传从该主键值之后开始
    private Integer getLastIId(String uploadTable){
        List<UploadRecord> uploadRecordList = uploadRecordService.selectLastByTableName(uploadTable);
        Integer lastProcessIId = 0;
        if( uploadRecordList != null && uploadRecordList.size()>0 )
            lastProcessIId = uploadRecordList.get(0).getTable_iid();
        if( lastProcessIId == null ) lastProcessIId = 0;
        return lastProcessIId;
    }

    /**
     * 本地表 upload_process 保存每一缸，一缸一行数据
     * 返回值有三种情况：
     * 0 - 当前行刷卡工序在颜色维度、缸号维度都是落后的，不需要上传
     * 1 - 当前行刷卡工序在颜色维度落后，在缸号维度是前进的，那么仅上传数据到 client_pushlist
     * 2 - 当前行刷卡工序在颜色维度、缸号维度都是前进的，那么上传两个数据表：client_pushlist 、 client_pushlist_Detail
     * @param processYield
     * @return
     */
    private int updateUploadProcess(GCjflowbilldetailprocess processYield){
        //传入 G_CJFlowBillDetailProcess.B_ItemID，查询构造出表 upload_process 结构的数据
        List<UploadProcess> list = uploadProcessService.getOrderPlanByProcessIId(processYield.getB_ItemID());
        if( list == null || list.size()<=0 ) return 0;// 没有构造出数据表示数据异常，直接返回不推送的结果
        UploadProcess upNew = list.get(0);// 如果查询获取到数据，则取出第一行储存
        // 从表 upload_process 中查询获取指定缸号的行数据
        List<UploadProcess> list1 = uploadProcessService.selectSpecVatnum(upNew.getOrder_iid(),upNew.getPlan_iid());

        // 如果上面获取到数据则赋值给 upCur，相反则创建一个空对象
        UploadProcess upCur = new UploadProcess();
        if( list1 != null && list1.size()>0 )
            upCur = list1.get(0);

        // 查询获取工序序号
        String vatNum = upNew.getVat_num();
        Integer newIndex = 0;
        Integer curIndex = 0;
        // 根据缸号查询获取旗下的所有工序以及排序序号
        List<PlanProcess> ppList = uploadProcessService.getProcessByVatnum(vatNum);
        if( ppList == null || ppList.size()<=0 ) return 0;// 查询不到缸号对应的工序列表表示数据异常
        // 先后获取：1. 当前刷卡的工序在功序列表中的序号 2. 表 upload_process 当前最新的工序
        for(int i=0;i<ppList.size();i++){
            if( ppList.get(i).getProcess_name().equals(upNew.getProcess_name()) )
                newIndex = ppList.get(i).getSeq();
            if( ppList.get(i).getProcess_name().equals(upCur.getProcess_name()) )
                curIndex = ppList.get(i).getSeq();
        }
        // 最新的刷卡工序排序小于表 upload_process 时表示当前工序滞后刷卡了
        // 在颜色维度、缸号维度都不需要推送，那么返回0
        if( newIndex<=curIndex ) {
            processYieldService.setUpload(1,processYield.getB_ItemID());// 设置本条刷卡记录已经为上传遍历过
            return 0;
        }

        upCur.setBill_code(upNew.getBill_code());
        upCur.setOrder_iid(upNew.getOrder_iid());
        upCur.setPlan_iid(upNew.getPlan_iid());
        upCur.setVat_num(upNew.getVat_num());
        upCur.setProcess_iid(upNew.getProcess_iid());
        upCur.setProcess_name(upNew.getProcess_name());
        upCur.setProcess_index(newIndex);
        upCur.setYield_time(upNew.getYield_time());
        upCur.setPlan_time(upNew.getPlan_time());
        upCur.setOrder_time(upNew.getOrder_time());
        upCur.setReleaseDate(upNew.getReleaseDate());

        // 查询颜色维度上是否有超过当前工序序号的缸号存在
        // 如果有说明已经有其他缸号工序的刷卡进度超过本缸号当前的工序，那么返回1表示仅更新当前缸号的进度，仅更新表 client_pushlist_detail
        // 相反，如果没有说明本缸号的本工序推动了颜色维度的工序进度，返回2，将更新两个表：client_pushlist 和 client_pushlist_detail
        List<UploadProcess> list2 = uploadProcessService.selectOrderAfterProcess(upNew.getOrder_iid(),newIndex,upNew.getReleaseDate());

        // 使用新值修改当前值后，将其写回表 upload_process
        int save = uploadProcessService.save(upCur);
        processYieldService.setUpload(1,processYield.getB_ItemID());// 设置本条刷卡记录已经为上传遍历过
        return list2.size()>0?1:2;
    }
}
